package com.dh.cycilc;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.junit.Test;
/**
 * CyclicBarrier的应用场景

CyclicBarrier常用于多线程分组计算。

2.CyclicBarrier方法说明
CyclicBarrier提供的方法有：

——CyclicBarrier(parties)

	初始化相互等待的线程数量的构造方法。

——CyclicBarrier(parties,Runnable barrierAction)

	初始化相互等待的线程数量以及屏障线程的构造方法。
	
	屏障线程的运行时机：等待的线程数量=parties之后，CyclicBarrier打开屏障之前。
	
	举例：在分组计算中，每个线程负责一部分计算，最终这些线程计算结束之后，交由屏障线程进行汇总计算。

——getParties()

	获取CyclicBarrier打开屏障的线程数量。

——getNumberWaiting()

	获取正在CyclicBarrier上等待的线程数量。

——await()

	在CyclicBarrier上进行阻塞等待，直到发生以下情形之一：
	
	在CyclicBarrier上等待的线程数量达到parties，则所有线程被释放，继续执行。
	当前线程被中断，则抛出InterruptedException异常，并停止等待，继续执行。
	其他等待的线程被中断，则当前线程抛出BrokenBarrierException异常，并停止等待，继续执行。
	其他等待的线程超时，则当前线程抛出BrokenBarrierException异常，并停止等待，继续执行。
	其他线程调用CyclicBarrier.reset()方法，则当前线程抛出BrokenBarrierException异常，并停止等待，继续执行。
——await(timeout,TimeUnit)

	在CyclicBarrier上进行限时的阻塞等待，直到发生以下情形之一：
	
	在CyclicBarrier上等待的线程数量达到parties，则所有线程被释放，继续执行。
	当前线程被中断，则抛出InterruptedException异常，并停止等待，继续执行。
	当前线程等待超时，则抛出TimeoutException异常，并停止等待，继续执行。
	其他等待的线程被中断，则当前线程抛出BrokenBarrierException异常，并停止等待，继续执行。
	其他等待的线程超时，则当前线程抛出BrokenBarrierException异常，并停止等待，继续执行。
	其他线程调用CyclicBarrier.reset()方法，则当前线程抛出BrokenBarrierException异常，并停止等待，继续执行。
——isBroken()

	获取是否破损标志位broken的值，此值有以下几种情况：
	
	CyclicBarrier初始化时，broken=false，表示屏障未破损。
	如果正在等待的线程被中断，则broken=true，表示屏障破损。
	如果正在等待的线程超时，则broken=true，表示屏障破损。
	如果有线程调用CyclicBarrier.reset()方法，则broken=false，表示屏障回到未破损状态。
——reset()

	使得CyclicBarrier回归初始状态，直观来看它做了两件事：
	
	如果有正在等待的线程，则会抛出BrokenBarrierException异常，且这些线程停止等待，继续执行。
	将是否破损标志位broken置为false。
 * @author liguobao
 *
 */
public class CyckicBarrierDemo1 {
	//构造函数1：初始化-开启屏障的方数
	static CyclicBarrier barrier0 = new CyclicBarrier(2);
	//通过barrier.getParties()获取开启屏障的方数
	
	public  static void main(String args[]) throws InterruptedException {
		//通过barrier.getParties()获取开启屏障的方数
		System.out.println("barrier.getParties()获取开启屏障的方数："+barrier0.getParties());;
		//通过barrier.getNumberWaiting()获取正在等待的线程数
		System.out.println("通过barrier.getNumberWaiting()获取正在等待的线程数：初始----" + barrier0.getNumberWaiting());;
		
		new Thread(() -> {
		    //添加一个等待线程
		   System.out.println("添加第1个等待线程----" + Thread.currentThread().getName());
		    try {
		    	barrier0.await();
		        System.out.println(Thread.currentThread().getName() + " is running...");
		    } catch (InterruptedException e) {
		        e.printStackTrace();
		    } catch (BrokenBarrierException e) {
		        e.printStackTrace();
		    }
		    System.out.println(Thread.currentThread().getName() + " is terminated.");
		}).start();
		
		
		Thread.sleep(10);
		//通过barrier.getNumberWaiting()获取正在等待的线程数
		System.out.println("通过barrier.getNumberWaiting()获取正在等待的线程数：添加第1个等待线程---" + barrier0.getNumberWaiting());
		Thread.sleep(10);
		System.out.println();
		new Thread(() -> {
		    //添加一个等待线程
		    System.out.println("添加第2个等待线程----" + Thread.currentThread().getName());
		    try {
		        barrier0.await();
		        System.out.println(Thread.currentThread().getName() + " is running...");
		    } catch (InterruptedException e) {
		        e.printStackTrace();
		    } catch (BrokenBarrierException e) {
		        e.printStackTrace();
		    }
		    System.out.println(Thread.currentThread().getName() + " is terminated.");
		}).start();
		Thread.sleep(100);
		System.out.println();
		//通过barrier.getNumberWaiting()获取正在等待的线程数
		System.out.println("通过barrier.getNumberWaiting()获取正在等待的线程数：打开屏障之后---" + barrier0.getNumberWaiting());
		
	}
	/**
	 * 熟悉reset()的用法
	      理解回归初始状态的意义
	      使得CyclicBarrier回归初始状态，直观来看它做了两件事：
			如果有正在等待的线程，则会抛出BrokenBarrierException异常，且这些线程停止等待，继续执行。
			将是否破损标志位broken置为false。
	 * @throws InterruptedException 
	 */
	//@Test
	public void cyclic1() throws InterruptedException {
		CyclicBarrier barrier2 = new CyclicBarrier(2);
		//如果是一个初始的CyclicBarrier，则reset()之后，什么也不会发生
		System.out.println("如果是一个初始的CyclicBarrier，则reset()之后，什么也不会发生");
		barrier2.reset();
		System.out.println();

		Thread.sleep(100);
		//如果是一个已经打开一次的CyclicBarrier，则reset()之后，什么也不会发生
		ExecutorService executorService2 = Executors.newCachedThreadPool();
		//等待两次
		for (int i = 0; i < 2; i++) {
		    executorService2.submit(() -> {
		        try {
		            barrier2.await();
		           System.out.println("222屏障已经打开.");
		        } catch (InterruptedException e) {
		            //e.printStackTrace();
		            System.out.println("222被中断");
		        } catch (BrokenBarrierException e) {
		            //e.printStackTrace();
		            System.out.println("222被重置");
		        }
		    });
		}
		barrier2.reset();

		Thread.sleep(100);
		System.out.println();
		//如果是一个 有线程正在等待的线程，则reset()方法会使正在等待的线程抛出异常
		executorService2.submit(() -> {
		    executorService2.submit(() -> {
		        try {
		            barrier2.await();
		            System.out.println("333屏障已经打开.");
		        } catch (InterruptedException e) {
		            //e.printStackTrace();
		        	 System.out.println("333被中断");
		        } catch (BrokenBarrierException e) {
		        	 System.out.println("在等待过程中，执行reset()方法，等待的线程抛出BrokenBarrierException异常，并不再等待");
		            //e.printStackTrace();
		        }
		    });
		});
		Thread.sleep(100);
		barrier2.reset();
		executorService2.shutdown();
	}
	
	/**
	 * 练习await()/await(timeout,TimeUnit)/isBroken()的使用方法
	       理解破损标志位broken的状态转换
	 * @throws InterruptedException
	 */
	@Test
	public void cyclic2() throws InterruptedException {
		CyclicBarrier barrier1 = new CyclicBarrier(3);
		ExecutorService executorService = Executors.newCachedThreadPool();
		//添加一个用await()等待的线程
		executorService.submit(() -> {
		    try {
		        //等待，除非：1.屏障打开;2.本线程被interrupt;3.其他等待线程被interrupted;4.其他等待线程timeout;5.其他线程调用reset()
		        barrier1.await();
		    } catch (InterruptedException e) {
		    	System.out.println(Thread.currentThread().getName() + " is interrupted.");
		        //e.printStackTrace();
		    } catch (BrokenBarrierException e) {
		    	System.out.println(Thread.currentThread().getName() + " is been broken.");
		        //e.printStackTrace();
		    }
		});
		Thread.sleep(10);
		System.out.println("刚开始，屏障是否破损：" + barrier1.isBroken());
		//添加一个等待线程-并超时
		executorService.submit(() -> {
		    try {
		        //等待1s，除非：1.屏障打开(返回true);
		    	//2.本线程被interrupt;
		    	//3.本线程timeout;
		    	//4.其他等待线程被interrupted;
		    	//5.其他等待线程timeout;6.其他线程调用reset()
		        barrier1.await(1, TimeUnit.SECONDS);
		    } catch (InterruptedException e) {
		    	System.out.println(Thread.currentThread().getName() + " is interrupted.");
		        //e.printStackTrace();
		    } catch (BrokenBarrierException e) {
		    	System.out.println(Thread.currentThread().getName() + " is been reset().");
		        //e.printStackTrace();
		    } catch (TimeoutException e) {
		    	System.out.println(Thread.currentThread().getName() + " is timeout.");
		        //e.printStackTrace();
		    }
		});
		Thread.sleep(100);
		System.out.println("当前等待线程数量：" + barrier1.getNumberWaiting());
		Thread.sleep(1000);
		System.out.println("当前等待线程数量：" + barrier1.getNumberWaiting());
		System.out.println("当等待的线程timeout时，当前屏障是否破损：" + barrier1.isBroken());
		System.out.println("等待的线程中，如果有一个出现问题，则此线程会抛出相应的异常；其他线程都会抛出BrokenBarrierException异常。");

		System.out.println();
		Thread.sleep(5000);
		//通过reset()重置屏障回初始状态，也包括是否破损
		barrier1.reset();
		System.out.println("reset()之后，当前屏障是否破损：" + barrier1.isBroken());
		System.out.println("reset()之后，当前等待线程数量：" + barrier1.getNumberWaiting());
		executorService.shutdown();
	}
	

}
